package main

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"math"
	"net"
	"os"
	"strconv"
	"strings"

	"github.com/bitly/go-simplejson"
)

var c = make(map[string]map[uint32]uint32)
var cc = make([]map[string]map[uint32]uint32, 30)
var cos = make(map[string]float64)
var overchan = make(chan int, 30)

func main() {
	// getPath()
	initcc()
	goDeal(getPath())
	collectDeal()
	genCos()
	w2file()
	// t()
}
func initcc() {
	for idx := range cc {
		cc[idx] = make(map[string]map[uint32]uint32)
	}
}
func goDeal(paths []string) {
	l := len(paths)
	gorountinenums := 30
	base := 0
	i := 0
	step := l / gorountinenums
	if l%gorountinenums != 0 {
		step++
	}
	for ; base < l; base += step {
		var files []string
		if base+step < l {
			files = paths[base : base+step]
		} else {
			files = paths[base:]
		}
		fmt.Println("-----------", i+1, "running")
		go dealView(files, cc[i], strconv.Itoa(i))
		i++
	}
}
func collectDeal() {
	loop := 0
	for ; loop < 30; loop++ {
		fmt.Println("__--__--__--__--__--__--__--___-", loop+1)
		<-overchan
	}
	c = mapADD(cc)
}
func dealView(files []string, c map[string]map[uint32]uint32, idx string) {
	i := 1
	filepath := "/viewlog/" + idx + ".log"
	fmt.Println(filepath)
	// goroutinefile, errr := os.Create(filepath)
	// if errr != nil {
	// 	panic(errr.Error())
	// }
	fmt.Println(idx + " routine is openning " + filepath)
	for _, filename := range files {
		fmt.Println(idx, " dealing with ", filename)
		f, errr := os.Open(filename)
		if errr != nil {
			fmt.Println(idx, " dealing with wrong ", filename)
			continue
		}
		scanner := bufio.NewScanner(f)
		for scanner.Scan() {
			json, err := simplejson.NewJson(scanner.Bytes())
			if err != nil {
				fmt.Println("aaaa~!~!~@1", err.Error())
				continue
			}
			i++
			pubid := json.Get("pubID").MustString("Unknown")
			ipad := net.ParseIP(json.Get("ip").MustString("Unknown"))
			ipad = ipad.To4()
			var ipadnum uint32
			if ipad != nil {
				ipadnum = (binary.BigEndian.Uint32(ipad)) % 1000000
			} else {
				continue
			}
			if val, ok := c[pubid]; ok {
				if _, iok := val[ipadnum]; iok {
					val[ipadnum]++
				} else {
					val[ipadnum] = 1
				}
			} else {
				c[pubid] = make(map[uint32]uint32)
				c[pubid][ipadnum]++
			}
		}
		fmt.Println(filename, "--------------over")
		f.Close()
	}
	fmt.Println("routine ", idx, "writing ---------", i, len(c))
	// for k, v := range c {
	// 	goroutinefile.Write([]byte(k + "::"))
	// 	for ik, iv := range v {
	// 		ipstr := strconv.FormatUint(uint64(ik), 10)
	// 		countstr := strconv.FormatUint(uint64(iv), 10)
	// 		goroutinefile.Write([]byte(ipstr + "->" + countstr + ";"))
	// 	}
	// 	goroutinefile.Write([]byte("\n"))
	// }
	// goroutinefile.Close()
	overchan <- 1
}

func w2file() {
	f, err := os.Create("/viewlog/ipcos_goroutine.csv")
	if err != nil {
		panic(err.Error())
	}
	fmt.Println("last result ---------")
	pubids := getAllPubids()

	lineone := "id," + strings.Join(pubids, ",") + "\n"
	f.Write([]byte(lineone))
	len := len(pubids)
	for i := 0; i < len; i++ {
		f.Write([]byte(pubids[i]))
		for j := 0; j < len; j++ {
			f.Write([]byte(","))
			if i < j {
				f.Write([]byte(strconv.FormatFloat(cos[pubids[i]+"::"+pubids[j]], 'f', -1, 64)))
			} else if i == j {
				f.Write([]byte("1"))
			} else {
				f.Write([]byte(strconv.FormatFloat(cos[pubids[j]+"::"+pubids[i]], 'f', -1, 64)))
			}
		}
		f.Write([]byte("\n"))
	}
}

func genCos() {
	pubids := getAllPubids()
	len := len(pubids)
	for i := 0; i < len-1; i++ {
		for j := i + 1; j < len; j++ {
			cos[pubids[i]+"::"+pubids[j]] = calcCos(pubids[i], pubids[j])
		}
	}
}

func getAllPubids() (re []string) {
	re = make([]string, 0, 300)
	for k := range c {
		re = append(re, k)
	}
	return
}

func calcLen(pubid string) float64 {
	var sum uint32
	for _, v := range c[pubid] {
		sum += (v * v)
	}
	return math.Sqrt(float64(sum))
}

func calcNeiji(pubida, pubidb string) uint32 {
	var sum uint32
	for k, v := range c[pubida] {
		if vb, ok := c[pubidb][k]; ok {
			sum += v * vb
		}
	}
	return sum
}

func calcCos(pubida, pubidb string) float64 {
	lena := calcLen(pubida)
	lenb := calcLen(pubidb)
	neiji := calcNeiji(pubida, pubidb)
	return float64(neiji) / (lena * lenb)
}

func getPath() []string {
	re := []string{}
	f, _ := os.Open("/paths.txt")
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		one := scanner.Text()
		if len(one) > 33 {
			continue
		}
		re = append(re, `/`+one)
	}
	f.Close()
	fmt.Println(re)
	return re
}

func mapADD(allmap []map[string]map[uint32]uint32) (re map[string]map[uint32]uint32) {
	re = make(map[string]map[uint32]uint32)
	fmt.Println("map add ing -----")
	pubids := getAllpubid(allmap)
	for _, v := range pubids {
		ipmap := fromPubGetAllIP(v, allmap)
		ips := getAllIP(ipmap)
		imap := make(map[uint32]uint32)
		for _, eachCount := range ipmap {
			for _, eachIP := range ips {
				if count, ok := eachCount[eachIP]; ok {
					if _, iok := imap[eachIP]; iok {
						imap[eachIP] += count
					} else {
						imap[eachIP] = count
					}
				}
			}
		}
		re[v] = imap
	}
	return
}
func fromPubGetAllIP(pubid string, allmap []map[string]map[uint32]uint32) (re []map[uint32]uint32) {
	re = make([]map[uint32]uint32, 0, 10000)
	for _, v := range allmap {
		if iv, ok := v[pubid]; ok {
			re = append(re, iv)
		}
	}
	return
}
func getAllpubid(allmap []map[string]map[uint32]uint32) (re []string) {
	re = make([]string, 0, 300)
	tmpmap := make(map[string]int)
	for _, v := range allmap {
		for k := range v {
			tmpmap[k] = 1
		}
	}
	for k := range tmpmap {
		re = append(re, k)
	}
	return
}
func getAllIP(allIP []map[uint32]uint32) (re []uint32) {
	re = make([]uint32, 0, 10000)
	tmpmap := make(map[uint32]int)
	for _, v := range allIP {
		for k := range v {
			tmpmap[k] = 1
		}
	}
	for k := range tmpmap {
		re = append(re, k)
	}
	return re
}
